﻿/*
 * Beat Kiener, kiener@glauxsoft.com, 06. April 2009
 */

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Browser;
using System.Collections.Generic;
using System.Windows.Automation.Peers;
using System.Windows.Automation;
using System.Windows.Automation.Provider;

namespace MouseWheelScrolling
{
    public static class MouseWheel
    {
        /// <summary>
        /// the current mouse position
        /// </summary>
        private static Point currentPoint;
        private static UIElement s_rootElement;

        static MouseWheel()
        {
            // register events (for differet browsers)
            HtmlPage.Window.AttachEvent("DOMMouseScroll", mouse_WheelTurned);
            HtmlPage.Window.AttachEvent("onmousewheel", mouse_WheelTurned);
            HtmlPage.Document.AttachEvent("onmousewheel", mouse_WheelTurned);
        }

        public static void Enable(UIElement rootElement)
        {
            // prevent null reference exception
            if (rootElement == null)
                return;

            // only one root element possible with this implementation
            if (s_rootElement != null)
                s_rootElement.MouseMove -= new MouseEventHandler(rootElement_MouseMove);

            s_rootElement = rootElement;
            s_rootElement.MouseMove += new MouseEventHandler(rootElement_MouseMove);

        }

        static void rootElement_MouseMove(object sender, MouseEventArgs e)
        {
            currentPoint = e.GetPosition(null);
        }

        private static void mouse_WheelTurned(Object sender, HtmlEventArgs args)
        {
            double delta = 0;
            ScriptObject e = args.EventObject;

            if (e.GetProperty("wheelDelta") != null) // IE and Opera
            {
                delta = ((double)e.GetProperty("wheelDelta"));
                if (HtmlPage.Window.GetProperty("opera") != null)
                    delta = -delta;
            }
            else if (e.GetProperty("detail") != null) // Mozilla and Safari
            {
                delta = -((double)e.GetProperty("detail"));
            }

            if (delta != 0)
            {
                args.PreventDefault();
                e.SetProperty("returnValue", false);

                // go through all element beneath the current mouse position
                IEnumerable<UIElement> elements = VisualTreeHelper.FindElementsInHostCoordinates(currentPoint, s_rootElement);
                foreach (UIElement element in elements)
                {
                    // get automation peer (if already created for this control)
                    AutomationPeer automationPeer = FrameworkElementAutomationPeer.FromElement(element);
                    if (automationPeer == null)
                    {
                        // create automation peer for element
                        automationPeer = FrameworkElementAutomationPeer.CreatePeerForElement(element);
                    }

                    //expect null: some elements doesn't have an automation peer implemented
                    if (automationPeer != null)
                    {
                        // horizontal scrolling?
                        bool ctrlKey = (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control;

                        // try to get scroll provider
                        // note: TextBoxAutomationPeer does not implement IScrollProvider
                        IScrollProvider scrollProvider = automationPeer.GetPattern(PatternInterface.Scroll) as IScrollProvider;
                        if (scrollProvider != null)
                        {
                            // set scoll amount
                            ScrollAmount scrollAmount = ScrollAmount.NoAmount;
                            if (delta < 0)
                                scrollAmount = ScrollAmount.SmallIncrement;
                            else if (delta > 0)
                                scrollAmount = ScrollAmount.SmallDecrement;

                            // is scrolling horizontal possible
                            if (scrollProvider.HorizontallyScrollable && ctrlKey)
                            {
                                scrollProvider.Scroll(scrollAmount, System.Windows.Automation.ScrollAmount.NoAmount);

                                // break the further serach in the uielement collection
                                break; // foreach
                            }
                            else if (scrollProvider.VerticallyScrollable)
                            {
                                scrollProvider.Scroll(System.Windows.Automation.ScrollAmount.NoAmount, scrollAmount);

                                // break the further serach in the uielement collection
                                break; // foreach
                            }

                            // don't break here, because of encapsulated scroll viewers such as in the treeview from the sl-toolkit
                            //break;
                        }
                    }
                }
            }
        }
    }
}
